#ifndef __UMMANAGE_H__
#define __UMMANAGE_H__

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

#ifdef TEST_UMMANAGE
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "log.h"

#define TEST_PRINT(fmt, ...) LOG(fmt, ##__VA_ARGS__)

#define TEST_EXIT(code)                          \
    do                                           \
    {                                            \
        TEST_PRINT("TEST_EXIT code=%d\n", code); \
        exit(code);                              \
    } while (0)

#define TEST_ASSERT(expr)       \
    do                          \
    {                           \
        if (!(expr))            \
        {                       \
            TEST_EXIT(SIGSEGV); \
        }                       \
    } while (0)

#define TEST_ASSERT_INFO(expr, fmt, ...)    \
    do                                      \
    {                                       \
        if (!(expr))                        \
        {                                   \
            TEST_PRINT(fmt, ##__VA_ARGS__); \
            TEST_EXIT(SIGSEGV);             \
        }                                   \
    } while (0)

#else
#define TEST_PRINT(fmt, ...)
#define TEST_EXIT(code)
#define TEST_ASSERT(expr)
#define TEST_ASSERT_INFO(expr, fmt, ...)
#endif

#ifdef __cplusplus
extern "C"
{
#endif

    enum um_type
    {
        UM_UNUSED = 0,
        UM_CALL,
        UM_RET,
    };

#pragma pack(1)
    typedef struct
    {
        int64_t op_id;
        uint32_t func_id; // 如果是UM_RET类型，则无用
        int param_len;
        enum um_type type;
    } op_t;
#pragma pack()

    /**
     * @brief 注册 untrusted memory，addr是虚拟地址，size是大小。
     * 由于host和enclave的虚拟地址空间是分开的，
     * 所以host和enclave的虚拟地址空间都需要注册。
     */
    void register_um(uint64_t addr, uint32_t size);

    /**
     * @brief 安全世界和非安全世界都需要进行一次初始化
     */
    void init_um(bool is_enclave);

    /**
     * @brief 将ECALL调用使用到的参数数据写入 EBUFFER
     * @brief func_id [in] 要调用的函数ID
     * @brief param [in] 要传递的参数指针
     * @brief param_len [in] 要传递的参数长度
     * @return 返回值为-1表示失败，否则成功，是op_id
     */
    int64_t async_ecall(uint32_t func_id, void *param, int param_len);
    /**
     * @brief 将ECALL调用的返回值写入 OBUFFER
     * @brief op_id [in] 调用该函数时返回的op_id
     * @brief ret [in] 需要返回的数据指针
     * @brief ret_len [in] 返回值的长度
     * @return 返回值为-1表示失败，0表示成功
     */
    int async_eret(int64_t op_id, void *ret, int ret_len);

    /**
     * @brief 将OCALL调用使用到的参数数据写入 OBUFFER
     * @brief func_id [in] 要调用的函数ID
     * @brief param [in] 要传递的参数指针
     * @brief param_len [in] 要传递的参数长度
     * @return 返回值为-1表示失败，否则成功，是op_id
     */
    int64_t async_ocall(uint32_t func_id, void *param, int param_len);
    /**
     * @brief 将OCALL调用的返回值写入 EBUFFER
     * @brief op_id [in] 调用该函数时返回的op_id
     * @brief ret [in] 需要返回的数据指针
     * @brief ret_len [in] 返回值的长度
     * @return 返回值为-1表示失败，0表示成功
     */
    int async_oret(int64_t op_id, void *ret, int ret_len);

    /**
     * @brief 查找ecall或者oret
     * @return 返回值为op_t，如果找到了则 op_t->type != UM_UNUSED
     */
    op_t find_ecall_or_oret();

    /**
     * @brief 查找ecall或者oret
     * @return 返回值为op_t，如果找到了则 op_t->type != UM_UNUSED
     */
    op_t find_ocall_or_eret();

    /**
     * @brief 读取数据, 会同时读取_op和data。 如果调用了该函数，则不允许调用 drop_ebuffer_data
     * @param dst [out] 保存读取出的数据指针
     * @param _op [out] 必须是 call_op_t/ret_op_t 类型的指针，保存读取出的header
     * @return 如果读取成果，返回值为true，否则返回false
     */
    bool read_ecall_or_oret(uint8_t *dst, op_t *op);
    /**
     * @brief 读取数据, 会同时读取_op和data。如果调用了该函数，则不允许调用 drop_obuffer_data
     * @param dst [out] 保存读取出的数据指针
     * @param _op [out] 必须是 call_op_t/ret_op_t 类型的指针，保存读取出的header
     * @return 如果读取成果，返回值为true，否则返回false
     */
    bool read_ocall_or_eret(uint8_t *dst, op_t *op);

    /**
     * @brief 删除环形缓冲区首部的数据，如果环形缓冲区没有数据，则不做任何操作
    */
    void drop_obuffer_data();

    /**
     * @brief 删除环形缓冲区首部的数据，如果环形缓冲区没有数据，则不做任何操作
    */
    void drop_ebuffer_data();

    uint32_t get_num_of_ecall_and_oret();
    uint32_t get_num_of_ocall_and_eret();

#ifdef __cplusplus
}
#endif

#endif // __UMMANAGE_H__